package com.hq.ims.home.controller;

import com.hq.ims.data.alisms.AliSmsConfig;
import com.hq.ims.data.entity.Area;
import com.hq.ims.data.entity.Company;
import com.hq.ims.data.entity.Dict;
import com.hq.ims.data.service.BaseAreaService;
import com.hq.ims.data.service.BaseCompanyService;
import com.hq.ims.data.service.BaseDictService;
import com.hq.ims.data.service.BaseUserService;
import com.hq.ims.data.service.other.AliOcrBusService;
import com.hq.ims.data.utils.ErrMsgConstant;
import com.hq.ims.data.utils.LocalConstant;
import com.hq.ims.data.utils.StringRedisUtils;
import com.hq.ims.data.vo.AppFileMapstruct;
import com.hq.ims.data.vo.CompanyMapstruct;
import com.hq.ims.data.vo.DictMapstruct;
import com.hq.ims.data.vo.ocr.AliCardOcrVo;
import com.hq.ims.home.service.file.IFileService;
import com.hq.utils.exception.CommonException;
import com.hq.utils.util.*;
import com.hq.utils.vo.ReturnVo;
import com.hq.utils.vo.SelectVo;
import com.hq.web.annotations.GetJsonValue;
import com.hq.web.conf.bean.UserAttribute;
import com.hq.web.penum.EnumDeviceType;
import com.hq.web.utils.RedisUtils;
import com.hq.web.utils.WebUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Api(tags = "全局基础")
@RestController
@RequestMapping(value = LocalConstant.SYS_TYPE_PUBLIC_URL_PREFIX + "/base")
@Slf4j
public class GlobalBaseController {

    @Value("${logging.file.path}")
    private String fileSavePath;
    @Resource(name = "localFileServiceImpl")
    private IFileService fileService;
    @Resource
    private AliOcrBusService aliOcrService;
    @Resource
    private AliSmsConfig aliSmsConfig;
    @Resource
    private UserAttribute userAttribute;
    @Resource
    private BaseDictService baseDictService;
    @Resource
    private BaseAreaService baseAreaService;
    @Resource
    private BaseCompanyService baseCompanyService;
    @Resource
    private BaseUserService baseUserService;

    // ------------------------- 上传下载文件相关 --------------------------------------

    @ApiOperation(value = "上传单个文件文")
    @PostMapping(value = "/app/singleFileUpload")
    public ReturnVo<AppFileMapstruct.FileVo> singleFileUpload(
            MultipartFile file
    ) {
        AppFileMapstruct.FileVo fileVo = fileService.appUploadFile(file);
        ObjectUtils.isNullThrow(fileVo, "上传文件失败");
        return ReturnVo.buildSuccess(fileVo);
    }

    @ApiOperation(value = "批量上传文件", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/app/multipleUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ReturnVo<List<AppFileMapstruct.FileVo>> multipleUpload(
            @RequestParam("files") MultipartFile[] files
    ) {
        List<AppFileMapstruct.FileVo> fileVoList = fileService.appUploadFileList(files);
        return ReturnVo.buildSuccess(fileVoList);
    }

    @ApiOperation(value = "预览")
    @ApiImplicitParams({
    })
    @GetMapping(value = "/app/preview")
    public void previewFile(
            HttpServletResponse response,
            @RequestParam String fileId
    ) throws IOException {
        fileService.appFilePreview(fileId);
    }

    @ApiOperation(value = "单文件下载", notes = "单文件下载")
    @ApiImplicitParams({
    })
    @GetMapping("/app/download")
    public void appDownload(
            @RequestParam List<String> fileIdList
    ) {
        fileService.appFileDown(fileIdList);
    }

    // ------------------------- 身份证相关 --------------------------------------

    @ApiOperation(value = "身份证图片转文字", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileUrl", value = "文件路径", required = true),
    })
    @PostMapping(value = "/cardImageToOrcWord", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<AliCardOcrVo> cardImageToOrcWord(
            @GetJsonValue String imageBase64
    ) throws FileNotFoundException {
        AliCardOcrVo aliCardOrcVo = aliOcrService.httpCardOrcByBase64(imageBase64);
        if (aliCardOrcVo == null || StringUtils.isEmpty(aliCardOrcVo.getNum())) {
            throw CommonException.build("身份证识别错误");
        }
        return ReturnVo.buildSuccess(aliCardOrcVo);
    }

    // ------------------------- 短信相关 --------------------------------------

    @ApiOperation(value = "发送校验手机号短信", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/sendVerifyMobileSms", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<Boolean> sendVerifyMobileSms(@GetJsonValue(zh = "手机号码") String mobile) {
        //验证手机号格式
        BooleanUtils.isFalseThrow(RegexUtils.checkMobile(mobile), ErrMsgConstant.MOBILE_VALID_ERR);
        String shortTimeSendKey = "SMS:" + mobile;
        Boolean keyIsExists = StringRedisUtils.redisKeyIsExists(shortTimeSendKey);
        BooleanUtils.isTrueThrow(keyIsExists, "一分钟内不要重复发送短信");
        //发送校验手机号短信
        aliSmsConfig.sendVerifyMobileSms(mobile);
        StringRedisUtils.saveRedisKey(shortTimeSendKey, "1", 1);
        return ReturnVo.buildSuccess(true);
    }

    @ApiOperation(value = "验证用户的验证码是否正确", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/verifyMobileSms", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<Boolean> sendVerifyMobileSms(
            @GetJsonValue String mobile,
            @GetJsonValue String code
    ) {
        Boolean isPass = aliSmsConfig.checkVerifyMobileSms(mobile, code);
        if (isPass) {
            aliSmsConfig.deleteVerifyMobileSms(mobile);
        }
        return ReturnVo.buildSuccess(isPass);
    }

    // ------------------------- 图形验验证码 --------------------------------------

    private Random random = new Random();

    @ApiOperation(value = "获取滑块验证码", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/getSlideVerifyImage", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<Map<String, String>> getSlideVerifyImage(
    ) throws IOException {
        //获取终端类型
        EnumDeviceType device = WebUtils.getDevice();
        device = device == EnumDeviceType.UNKNOWN ? EnumDeviceType.PC : device;

        String dir = device.name.toLowerCase();
        //随机找一张图片
        int num = random.nextInt(6) + 1;
        String filePath = String.format("validimg/" + dir + "/%d.jpg", num);
        ClassPathResource classPathResource = new ClassPathResource(filePath);
        InputStream is = classPathResource.getInputStream();
        BufferedImage bi = ImageIO.read(is);
        int generateSlideWidth = 100;
        int generateSlideHeight = 100;
        if (device == EnumDeviceType.MOBILE) {
            generateSlideWidth = 50;
            generateSlideHeight = 50;
        }
        //生成
        ImageVerificationSlideChartUtils.ImageValidInfo imageValidInfo = ImageVerificationSlideChartUtils.getInstance().generate(bi, generateSlideWidth, generateSlideHeight);
        //转换对前给前端
        String backdropImageBase64 = Base64.encodeBase64String(imageValidInfo.getBackdropImageByte());
        String slideImageBase64 = Base64.encodeBase64String(imageValidInfo.getSlideImageByte());
        WebUtils.getRequest().getSession().setAttribute("correctPositionStartX", imageValidInfo.getCorrectPositionStartX());
        //返给前端的数据
        Map<String, String> resultMap = new HashMap<>(2);
        resultMap.put("backdropImageBase64", backdropImageBase64);
        resultMap.put("slideImageBase64", slideImageBase64);
        String sessionId = WebUtils.getRequest().getSession().getId();
        resultMap.put("randomValue", sessionId);
        if (userAttribute.getIsDebug()) {
            resultMap.put("saveOffset", String.valueOf(imageValidInfo.getCorrectPositionStartX()));
        }
        resultMap.put("saveOffset", String.valueOf(imageValidInfo.getCorrectPositionStartX()));
        RedisUtils.getRedisTemplate().opsForValue().set(LocalConstant.REDIS_KEY_PREFIX_IMAGE_SLIDE_VALID + sessionId, imageValidInfo.getCorrectPositionStartX(), LocalConstant.INT_NUM_FIVE, TimeUnit.MINUTES);
        return ReturnVo.buildSuccess(resultMap);
    }

    @ApiOperation(value = "验证滑块位置", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/validSlideVerifyImage", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<Boolean> getSlideVerifyImage(
            @GetJsonValue(zh = "唯一标识") String randomValue,
            @GetJsonValue(zh = "偏移量") Integer xOffset
    ) throws IOException {
        //转换对前给前端
        Object oriXOffset = ObjectUtils.isNullDefault(RedisUtils.getRedisTemplate().opsForValue().get(LocalConstant.REDIS_KEY_PREFIX_IMAGE_SLIDE_VALID + randomValue), Integer.MIN_VALUE);
        int oriXOffsetInt = (int) oriXOffset;
        BooleanUtils.isTrueThrow(oriXOffsetInt < 0, ErrMsgConstant.IMAGE_SLIDE_VALID_ERR);
        int faultTolerantValue = 10;
        if (xOffset < oriXOffsetInt - faultTolerantValue || xOffset > oriXOffsetInt + faultTolerantValue) {
            throw CommonException.build("拼图位置不正确");
        }
        return ReturnVo.buildSuccess(true);
    }

    // ------------------------- 字典表 --------------------------------------

    @ApiOperation(value = "根据父ID查询字典", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/getDictByParentId", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<List<Dict>> getDictByParentId(
            @GetJsonValue(required = false, df = "0") String parentId
    ) {
        List<Dict> list = baseDictService.getDictByParentId(parentId);
        return ReturnVo.buildSuccess(list);
    }

    @ApiOperation(value = "根据父ID查询返回树结构", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/getDictTreeByParentId", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<List<DictMapstruct.DictTreeVo>> getDictTreeByParentId(
            @GetJsonValue(required = false, df = "0") String parentId
    ) {
        List<DictMapstruct.DictTreeVo> dictTreeVoList = baseDictService.getDictTreeByParentId(parentId);
        return ReturnVo.buildSuccess(dictTreeVoList);
    }

    // ------------------------- 地区省市区 --------------------------------------

    @ApiOperation(value = "获取省市区", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/getNationAreaByParentId", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<List<SelectVo>> getNationAreaByParentId(
            @GetJsonValue(required = false, df = "0") String parentId
    ) {
        List<Area> list = baseAreaService.lambdaQuery().eq(Area::getParentId, parentId).list();
        List<SelectVo> selectVoList = SelectVoUtils.convertBeanToSelect(list, d -> d.getAreaId(), d -> d.getAreaName(), d -> d.getChildrenCount());
        return ReturnVo.buildSuccess(selectVoList);
    }

    // ------------------------- 公司 --------------------------------------

    @ApiOperation(value = "根据产业标记获取公司信息", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/getCompanySelectByCompanyServiceTypeId", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<List<CompanyMapstruct.CompanyVo>> getCompanySelectByCompanyServiceTypeId(
            @GetJsonValue(required = false, df = "0") String companyServiceTypeId
    ) {
        List<Company> companyList = baseCompanyService.getByCompanyServiceTypeId(companyServiceTypeId);
        //重组只返回简单信息
        List<CompanyMapstruct.CompanyVo> voList = ListUtils.newList(companyList.size());
        for (Company company : companyList) {
            CompanyMapstruct.CompanyVo vo = new CompanyMapstruct.CompanyVo();
            vo.setCompanyId(company.getCompanyId());
            vo.setCompanyName(company.getCompanyName());
            vo.setCompanyLevel(company.getCompanyLevel());
            voList.add(vo);
        }
        return ReturnVo.buildSuccess(voList);
    }

    @ApiOperation(value = "根据名称搜索获取公司信息", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/getCompanySelectByCompanyName", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<List<CompanyMapstruct.CompanyVo>> getCompanySelectByCompanyName(
            @GetJsonValue(required = false) String companyName
    ) {
        //如果搜索公司名称含关键字由返回空
        List<String> returnEmptyKeyWord = Arrays.asList("有限公司", "有限", "公司");
        if (returnEmptyKeyWord.contains(companyName)) {
            return ReturnVo.buildSuccess(ListUtils.newEmptyList());
        }
        //获取redis中的公司数据
        List<Company> companyList = baseCompanyService.getRedisAllCompanyList();
        //排序
        companyList = ListUtils.sortLocalDateTime(companyList, d -> d.getCreateDateTime(), true);
        //过滤出符合条件的公司
        companyList = companyList.stream().filter(d -> d.getCompanyName().contains(companyName)).collect(Collectors.toList());
        //重组只返回简单信息
        List<CompanyMapstruct.CompanyVo> voList = new ArrayList<>(companyList.size());
        for (Company company : companyList) {
            CompanyMapstruct.CompanyVo vo = new CompanyMapstruct.CompanyVo();
            vo.setCompanyId(company.getCompanyId());
            vo.setCompanyName(company.getCompanyName());
            vo.setCompanyLevel(company.getCompanyLevel());
            voList.add(vo);
        }
        return ReturnVo.buildSuccess(voList);
    }

    // ------------------------- 账号相关 --------------------------------------

    @ApiOperation(value = "手机号是否存在", consumes = MediaType.APPLICATION_JSON_VALUE)
    @ApiImplicitParams({
    })
    @PostMapping(value = "/mobileIsExists", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ReturnVo<Boolean> mobileIsExists(
            @GetJsonValue(zh = "手机号") String mobile
    ) {
        BooleanUtils.isFalseThrow(RegexUtils.checkMobile(mobile), ErrMsgConstant.MOBILE_VALID_ERR);
        boolean mobileExists = baseUserService.mobileExists(mobile, null);
        return ReturnVo.buildSuccess(mobileExists);
    }

}
